﻿using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace Parallel.Translate
{
    /// <summary>
    /// 百度翻译
    /// </summary>
    public sealed class BaiduTranslate : ITranslate
    {
        private readonly HttpClient _httpClient;
        private readonly Regex _lineBreaksPattern;
        private readonly BlockingCollection<TextItem> _queue;
        private readonly BaiduSettings _settings;


        /// <summary>
        /// 
        /// </summary>
        public BaiduTranslate()
        {
            var configBuilder = new ConfigurationBuilder().AddJsonFile("appsettings.json");
#if DEBUG
            configBuilder.AddJsonFile("appsettings.debug.json", true);
#endif
            _settings = configBuilder.Build().GetSection(nameof(BaiduSettings)).Get<BaiduSettings>();
            _lineBreaksPattern = new Regex("[\r\n]+", RegexOptions.Multiline);
            _httpClient = new HttpClient(new HttpClientHandler());
            _queue = new BlockingCollection<TextItem>();
            _ = ConsumeQueue();
        }

        /// <summary>
        /// 接收要翻译的文本
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public async Task<string> TranslateAsync(string text)
        {
            if (text == null)
            {
                _queue.CompleteAdding();
                return null;
            }
            // 使用source整形并回调
            var source = new TaskCompletionSource<string>();
            _queue.Add(new TextItem(source, Format(text)));
            try
            {
                var result = await source.Task;
                return result;
            }
            catch (Exception ex)
            {
                return ex.Message;
            }
        }
        /// <summary>
        /// 格式化文本
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        private string Format(string text)
        {
            return _lineBreaksPattern.Replace(text, " ").Trim();
        }

        /// <summary>
        /// 消费队列
        /// </summary>
        /// <returns></returns>
        private async Task ConsumeQueue()
        {
            int length = 0;
            bool timeout = false;
            Stopwatch watch = new Stopwatch();
            TimeSpan seconds = TimeSpan.FromSeconds(1);
            Dictionary<string, List<TaskCompletionSource<string>>> batch = null;
            watch.Start();
            while (!_queue.IsCompleted)
            {
                batch ??= new Dictionary<string, List<TaskCompletionSource<string>>>();
                if (_queue.TryTake(out var item, 1000))
                {
                    var key = item.Text.Trim();
                    length += key.Length;
                    if (batch.ContainsKey(key))
                        batch[key].Add(item.Source);
                    else
                        batch.Add(key, new List<TaskCompletionSource<string>> { item.Source });
                }
                else
                {
                    timeout = true;
                    await Task.Delay(200);
                }
                if (length > 5000 || batch.Count >= 200 || (batch.Count > 0 && timeout))
                {
                    // 百度一秒一下
                    var delay = seconds - watch.Elapsed;
                    if (delay.Ticks > 0) await Task.Delay(delay);
                    Console.WriteLine(batch.Count);
                    await BatchTranslateAsync(batch);
                    timeout = false;
                    batch = null;
                    length = 0;
                    watch.Restart();
                }
                if (_queue.Count == 0)
                    Console.WriteLine(".");
            }
        }
        /// <summary>
        /// 批量翻译
        /// </summary>
        /// <param name="batch"></param>
        /// <returns></returns>
        private async Task BatchTranslateAsync(Dictionary<string, List<TaskCompletionSource<string>>> batch)
        {
            var text = string.Join(Environment.NewLine, batch.Keys);
            var result = await SendAsync(text);
            var resultList = result.Trans_Result;
            if (resultList != null && resultList.Count > 0)
            {
                foreach (var trans in resultList)
                {
                    if (batch.TryGetValue(trans.Src, out var tasks))
                    {
                        tasks.ForEach(task => task.SetResult(trans.Dst));
                    }
                }
            }
            else
            {
                foreach (var tasks in batch.Values)
                {
                    tasks.ForEach(task => task.TrySetException(new ApplicationException(result.Error_Msg)));
                }
            }

        }
        /// <summary>
        /// 发送要翻译的文本
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        private async Task<BaiduResult> SendAsync(string text)
        {
            try
            {
                var request = BuildRequestMessage(text);
                var response = await _httpClient.SendAsync(request);
                var contentString = await response.Content.ReadAsStringAsync();
                return JsonConvert.DeserializeObject<BaiduResult>(contentString);
            }
            catch (Exception ex)
            {
                return new BaiduResult { Error_Msg = ex.Message };
            }
        }
        /// <summary>
        /// 生成请求消息
        /// </summary>
        /// <param name="queryText"></param>
        /// <returns></returns>
        private HttpRequestMessage BuildRequestMessage(string queryText)
        {
            var slat = queryText.GetHashCode().ToString();
            var sign = MD5Hash($"{_settings.AppId}{queryText}{slat}{_settings.AppKey}");
            var data = $"q={Uri.EscapeDataString(queryText)}&from={_settings.From}&to={_settings.To}&appid={_settings.AppId}&salt={slat}&sign={sign}";
            var request = new HttpRequestMessage(HttpMethod.Post, _settings.AppAddress)
            {
                Content = new StringContent(data, Encoding.UTF8, "application/x-www-form-urlencoded")
            };
            return request;
        }
        /// <summary>
        /// MD5-32
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        private string MD5Hash(string source)
        {
            try
            {
                var bytes = Encoding.UTF8.GetBytes(source);
                var hashs = MD5.Create().ComputeHash(bytes);
                return BitConverter.ToString(hashs).Replace("-", "").ToLower();
            }
            catch { }
            return string.Empty;
        }

        #region internal class
        /// <summary>
        /// 
        /// </summary>
        private class TextItem
        {
            public TaskCompletionSource<string> Source { set; get; }
            public string Text { set; get; }
            public TextItem(TaskCompletionSource<string> source, string text)
            {
                Text = text;
                Source = source;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        private class BaiduResult
        {
            [JsonProperty(PropertyName = "from")]
            public string From { set; get; }
            [JsonProperty(PropertyName = "to")]
            public string To { set; get; }
            [JsonProperty(PropertyName = "trans_result")]
            public IList<BaiduTransResult> Trans_Result { set; get; }
            [JsonProperty(PropertyName = "error_msg")]
            public string Error_Msg { set; get; }
            [JsonProperty(PropertyName = "error_code")]
            public int Error_Code { set; get; }
        }
        /// <summary>
        /// 
        /// </summary>
        private class BaiduTransResult
        {
            [JsonProperty(PropertyName = "src")]
            public string Src { set; get; }
            [JsonProperty(PropertyName = "dst")]
            public string Dst { set; get; }
        }
        private class BaiduSettings
        {
            public string AppId { set; get; }
            public string AppKey { set; get; }
            public string AppAddress { set; get; }
            public string From { set; get; } = "auto";
            public string To { set; get; }
        }
        #endregion
    }



    /// <summary>
    /// 翻译接口
    /// </summary>
    public interface ITranslate
    {
        /// <summary>
        /// 翻译文本
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        Task<string> TranslateAsync(string text);
    }
}
